package znet

import (
	"errors"
	"fmt"
	"net"
	"zinx/src/utils"
	"zinx/src/ziface"
)

// Server IServer接口的实现类
type Server struct {
	Name      string
	IPVersion string
	IP        string
	Port      int
	//当前Server的消息管理模块，用来绑定msgId和对应的处理方法
	msgHandler ziface.IMsgHandle
	ConnMgr    ziface.IConnManager
	//新增两个hook函数原型
	//该Server的连接创建时Hook函数
	OnConnStart func(conn ziface.IConnection)
	//该Server的连接断开时的Hook函数
	OnConnStop func(conn ziface.IConnection)
}

// SetOnConnStart 设置该Server的连接创建时Hook函数
func (s *Server) SetOnConnStart(hookFunc func(ziface.IConnection)) {
	s.OnConnStart = hookFunc
}

// SetOnConnStop 设置该Server的连接断开时的Hook函数
func (s *Server) SetOnConnStop(hookFunc func(ziface.IConnection)) {
	s.OnConnStop = hookFunc
}

// CallOnConnStart 调用连接OnConnStart Hook函数
func (s *Server) CallOnConnStart(conn ziface.IConnection) {
	if s.OnConnStart != nil {
		fmt.Println("---> CallOnConnStart....")
		s.OnConnStart(conn)
	}
}

// CallOnConnStop 调用连接OnConnStop Hook函数
func (s *Server) CallOnConnStop(conn ziface.IConnection) {
	if s.OnConnStop != nil {
		fmt.Println("---> CallOnConnStop....")
		s.OnConnStop(conn)
	}
}

func (s *Server) GetConnMgr() ziface.IConnManager {

	return s.ConnMgr
}

func (s *Server) AddRouter(msgId uint32, router ziface.IRouter) {
	//注册自定义的路由方法
	s.msgHandler.AddRouter(msgId, router)
	fmt.Println("Add router successful!")
}

// CallBackToClient 是指当前客户端连接的handle api
func CallBackToClient(conn *net.TCPConn, data []byte, cnt int) error {
	//回显业务
	fmt.Println("[Conn Handle] CallBackToClient...")
	if _, err := conn.Write(data[:cnt]); err != nil {
		//把数据写出到客户端的套接字中
		fmt.Println("write back buf err", err)
		return errors.New("CallBackToClient error")
	}
	return nil
}

// Start 开启网络服务;实现ziface.IServer里的全部方法
func (s *Server) Start() {
	fmt.Printf("Server name %s,[Start]Server Listenner at IP:%s,Port %d,is starting\n", s.Name, s.IP, s.Port)
	fmt.Printf("[Zinx] Version: %s, MaxConn: %d,  MaxPacketSize: %d\n",
		utils.GlobalObject.Version,
		utils.GlobalObject.MaxConn,
		utils.GlobalObject.MaxPacketSize)
	//在这里封装一个基本的服务器的开发流程
	//把整个业务放在go协程，这样当某个TCP连接循环阻塞时，不会导致整个服务器端阻塞
	go func() {
		//0.启动worker工作池机制
		s.msgHandler.StartWorkerPool()
		//1.获取1个TCP的Addr
		addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%d", s.IP, s.Port))
		if err != nil {
			fmt.Println("resolve tcp addr error : ", err)
			return
		}
		//2.监听服务器的Addr
		listener, err := net.ListenTCP(s.IPVersion, addr)
		if err != nil {
			fmt.Println("listen", s.IPVersion, "err:", err)
			return
		}
		fmt.Println("start Zinx server success", s.Name, "is listening!")
		//3.阻塞的等待客户端链接，处理客户端连接业务(读写)
		//TODO 应该有一个自动生成ID的方法
		var cid uint32
		cid = 0
		for {
			//如果有客户端链接过来，阻塞停止会返回一个套接字
			conn, err := listener.AcceptTCP()
			if err != nil {
				fmt.Println("Accept err", err)
				continue
				//此次连接建立失败，跳过等待下一次
			}
			//设置服务器的最大连接数，如果超过最大连接，关闭此新的连接
			if s.ConnMgr.Len() >= utils.GlobalObject.MaxConn {
				conn.Close()
				continue
			}
			//处理该新连接请求的业务方法,conn和handle绑定
			dealConn := NewConnection(s, conn, cid, s.msgHandler)
			cid++
			//启动当前连接的处理业务
			go dealConn.Start()
		}
	}()
}

func (s *Server) Stop() {
	//将一些服务器的资源、状态或者一些链接进行关闭回收
	fmt.Println("[STOP] Zinx server , name ", s.Name)
	s.ConnMgr.ClearConn()
}

func (s *Server) Serve() {
	//我们只开放Serve给使用者以启动服务监听某个端口
	s.Start() //start本身是异步的，不会阻塞
	//TODO 做一些启动服务器之后的额外业务
	//在这里把整个服务器陷入阻塞状态，一直服务
	select {}
	//调用了select方法但是没写选择条件进行判断
}

func NewServer(name string) ziface.IServer {
	//先初始化全局配置文件
	utils.GlobalObject.Reload()
	s := &Server{
		Name:       utils.GlobalObject.Name,
		IPVersion:  "tcp4",
		IP:         utils.GlobalObject.Host,
		Port:       utils.GlobalObject.TcpPort,
		msgHandler: NewMsgHandle(),
		ConnMgr:    NewConnManager(),
	}
	return s
}
